//
// Created by martin on 3/26/22.
//

#ifndef MUDUO_FTPSESSION_H
#define MUDUO_FTPSESSION_H

#include "UserInfo.h"
//#include "FtpDir.h"
//#include "FtpDataClient.h"
//#include "FtpDataServer.h"
#include "DataConnection.h"
#include "Filesystem.h"
#include "IoFile.h"

#include "muduo/base/noncopyable.h"
#include "muduo/net/InetAddress.h"
#include "muduo/net/EventLoop.h"
#include "muduo/base/Logging.h"

#include <memory>
#include <fstream>
#include <vector>

namespace ftp
{

typedef std::shared_ptr<FILE> FilePtr;

class FtpMessage;
class FtpRequest;
class UserDao;
enum class FtpReplyCode;

class FtpSession : muduo::noncopyable, public std::enable_shared_from_this<FtpSession>
{
public:
    using HandlerFuncType = void (FtpSession::*)(const std::string &);
    using HandlerItemType = std::pair<const char*, HandlerFuncType>;

#if 0
	/// RAII file stream
	struct IoFile
	{
		IoFile(const std::string& pathname, std::ios::openmode mode)
			: fileStream_(pathname, mode),
			streamBuffer_(1024 * 1024 * 1) // 1MB
		{
			fileStream_.rdbuf()->pubsetbuf(&streamBuffer_[0], static_cast<long>(streamBuffer_.size())); // set buffer for file stream
		}

		~IoFile() {
			LOG_DEBUG << "Destroy IoFile@" << this;
			fileStream_.close();
			fileStream_.flush();
		}

		std::fstream fileStream_;
		std::vector<char> streamBuffer_;
	};
#endif

	FtpSession(const muduo::net::TcpConnectionPtr& conn, const UserDao& userDao);

	~FtpSession();

	/// Start a new Ftp session. Invoke it to send greetins to client after control connection established
	void start();

	void handleFtpCommand(const FtpRequest& request);

	void setBinaryFlag(bool on)
	{
		dataBinaryFlag_ = on;
	}

	bool binaryFlag() const
	{
		return dataBinaryFlag_;
	}

	FilePtr& sendFilrPtr()
	{
		return sendFilePtr_;
	}
	void setSendFilePtr(FilePtr& filePtr)
	{
		sendFilePtr_ = filePtr;
	}

	FilePtr& receiveFilePtr()
	{
		return receiveFilePtr_;
	}
	void setRcvFilePtr(FilePtr& filePtr)
	{
		receiveFilePtr_ = filePtr;
	}

	bool receiveFileErrorFlag() const
	{
		return receiveFileErrorFlag_;
	}
	void setReceiveFileErrorFlag(bool errorFlag)
	{
		receiveFileErrorFlag_ = errorFlag;
	}

	const std::string& sendFileFullPath() const
	{
		return sendFileFullPath_;
	}
	void setSendFileFullPath(const std::string& path)
	{
		sendFileFullPath_ = path;
	}

	const std::string& receiveFileFullPath() const
	{
		return receiveFileFullPath_;
	}
	void setReceiveFileFullPath(const std::string& path)
	{
		receiveFileFullPath_ = path;
	}

private:

	// Send Ftp Message by control connection

	void sendFtpMessage(FtpReplyCode code, const std::string& message);
	void sendFtpMessage(const FtpMessage& message);
	void sendRawMessage(const std::string& rawMessage);

	//---------------------------------------------------------
	// Handle FTP commands
	//---------------------------------------------------------

	// Access control commands
	void handleFtpCommandUSER(const std::string& param);
	void handleFtpCommandPASS(const std::string& param);
	void handleFtpCommandACCT(const std::string& param);
	void handleFtpCommandCWD(const std::string& param);
	void handleFtpCommandCDUP(const std::string& param);
	void handleFtpCommandREIN(const std::string& param);
	void handleFtpCommandQUIT(const std::string& param);

	// Transfer parameter commands
	void handleFtpCommandPORT(const std::string& param);
	void handleFtpCommandPASV(const std::string& param);
	void handleFtpCommandTYPE(const std::string& param);
	void handleFtpCommandSTRU(const std::string& param);
	void handleFtpCommandMODE(const std::string& param);

	// Ftp service commands
	void handleFtpCommandRETR(const std::string& param);
	void handleFtpCommandSIZE(const std::string& param);
	void handleFtpCommandSTOR(const std::string& param);
	void handleFtpCommandSTOU(const std::string& param);
	void handleFtpCommandAPPE(const std::string& param);
	void handleFtpCommandALLO(const std::string& param);
	void handleFtpCommandREST(const std::string& param);
	void handleFtpCommandRNFR(const std::string& param);
	void handleFtpCommandRNTO(const std::string& param);
	void handleFtpCommandABOR(const std::string& param);
	void handleFtpCommandDELE(const std::string& param);
	void handleFtpCommandRMD(const std::string& param);
	void handleFtpCommandMKD(const std::string& param);
	void handleFtpCommandPWD(const std::string& param);
	void handleFtpCommandLIST(const std::string& param);
	void handleFtpCommandNLST(const std::string& param);
	void handleFtpCommandSITE(const std::string& param);
	void handleFtpCommandSYST(const std::string& param);
	void handleFtpCommandSTAT(const std::string& param);
	void handleFtpCommandHELP(const std::string& param);
	void handleFtpCommandNOOP(const std::string& param);

	// Modern Ftp Commands
	void handleFtpCommandFEAT(const std::string& param);
	void handleFtpCommandOPTS(const std::string& param);

	// Detail Ftp Commands executor for handling Ftp Commands
	FtpMessage executeCWD(const std::string& param);
	FtpMessage executePort(const std::string& param);
	FtpMessage executePasv(const std::string& param);

	//---------------------------------------------------------
	// Ftp data-socket send
	//---------------------------------------------------------
	void sendDirectoryListing(const std::string& targetPath, const std::map<std::string, Filesystem::FileStatus>& directoryContents);
	void sendFile(const std::string& targetPath, std::shared_ptr<ftp::filesystem::IoFile> file);
	void receiveFile(const std::string& targetPath, std::shared_ptr<ftp::filesystem::IoFile> file);
	std::string uniqueFilePath(const std::string& filePath);

	//---------------------------------------------------------
	// Control DataConnection resource
	//---------------------------------------------------------

	// close command socket
	void closeCommand();
	void openData(TransferMode mode,
                  const muduo::net::InetAddress& serverAddr,
                  const std::string& nameArg = std::string("DataConnection"));
	void closeData();
	bool isTransferData() const;

	//---------------------------------------------------------
	// Helpers
	//---------------------------------------------------------

	std::string toLocalPath(const std::string& ftpPath);
	std::string toAbsoluteFtpPath(const std::string& relativeOrAbsoluteFtpPath);
	FtpMessage checkIfPathIsRenameable(const std::string& ftpPath);
	static std::string quotedFtpPath(const std::string& ftpPath) ;

	bool authorized() const;

	muduo::net::EventLoop* loop_; // Owner EventLoop, i.e. IO Loop

	std::shared_ptr<UserInfo> loggedInUser_;
	std::string usernameForLogin_;

	bool dataBinaryFlag_; // binary flag for data connection
	bool portFlag_;       // port mode flag for data connection

	FilePtr sendFilePtr_;
	FilePtr receiveFilePtr_;
	bool receiveFileErrorFlag_;
	std::string sendFileFullPath_; // FTP full path, not physical path
	std::string receiveFileFullPath_; // FTP full path, not physical path

	std::string lastCommand_;
	std::string renameOldPath_; // source path for RNFR
	std::string renameNewPath_; // target path for RNTO

	const UserDao& ftpUserDao_; // operating user database

	// working state
	std::string ftpCurrentWorkingDirectory_;
	static const std::string kFtpRootDirectory;

	muduo::net::TcpConnectionWeakPtr conntrolConnWeakPtr_; // control connection weak pointer
	//FtpDataServerPtr dataServer_;
	//FtpDataClientPtr dataClient_;

	std::unique_ptr<DataConnection> dataConnection_;
	bool isTransferData_;

	// 对端数据通道地址, 用于PORT模式
	muduo::net::InetAddress dataPeerAddr;

    static std::vector<HandlerItemType> const handlers_;
};

} // namespace ftp

#endif //MUDUO_FTPSESSION_H
